using System;
using System.Linq.Expressions;
using System.ComponentModel;
using nRoute.Components;
using System.Runtime.Serialization;
using System.Windows.Markup;
using nRoute.Internal;

namespace nRoute.SiteMaps
{
    /// <summary>
    /// Represents a <see cref="SiteMap">Site Map</see> leaf, that can have zero or more sub-nodes.
    /// </summary>
    [DataContract]
    [ContentProperty("ChildNodes")]
    [KnownType(typeof(NavigationNode))]
    [KnownType(typeof(ControllerActionNode))]
    public abstract class SiteMapNode
        : INotifyPropertyChanged
		// : DependencyObject
    {
        private const string KEY_CANONLY_BESETONCE = "SiteMapNode with Key ('{0}') cannot be changed once set";
        
        private string _key;
        private NodesCollection _childNodes;
        private string _title;
        private string _description;
        private string _iconPath;
        private string _siteArea;
        private bool _isEnabled = true;     // by default 
        private bool _isListed = true;      // by default

#region Properties

        /// <summary>
        /// The unique key identfiying the <see cref="SiteMapNode">Site Map Node</see>.
        /// </summary>
        [DataMember(EmitDefaultValue = false)]
        public string Key
        {
            get { return _key; }
            set
            {
                if (!string.Equals(_key, value, StringComparison.InvariantCulture))
                {
                    _key = value;
                    NotifyPropertyChanged(() => Key);
                }
            }
        }

        /// <summary>
        /// The title of the node.
        /// </summary>
        [DataMember(EmitDefaultValue = false)]
        public string Title
        {
            get { return _title; }
            set
            {
                if (!string.Equals(_title, value, StringComparison.InvariantCulture))
                {
                    _title = value;
                    NotifyPropertyChanged(() => Title);
                }
            }
        }

        /// <summary>
        /// The description of the node.
        /// </summary>
        [DataMember(EmitDefaultValue = false)]
        public string Description
        {
            get { return _description; }
            set
            {
                if (!string.Equals(_description, value, StringComparison.InvariantCulture))
                {
                    _description = value;
                    NotifyPropertyChanged(() => Description);
                }
            }
        }

        /// <summary>
        /// The Icon Path for the node.
        /// </summary>
        public string IconPath
        {
            get { return _iconPath; }
            set
            {
                if (!string.Equals(_iconPath, value, StringComparison.InvariantCulture))
                {
                    _iconPath = value;
                    NotifyPropertyChanged(() => IconPath);
                }
            }
        }

        /// <summary>
        /// Child nodes of the nodes.
        /// </summary>
        [DataMember(EmitDefaultValue = false)]
        public NodesCollection ChildNodes
        {
            get
            {
                if (_childNodes == null)
                {
                    _childNodes = new NodesCollection();
                    NotifyPropertyChanged(() => ChildNodes);
                }
                return _childNodes;
            }
            set
            {
                if (_childNodes != value)
                {
                    _childNodes = value;
                    NotifyPropertyChanged(() => ChildNodes);
                }
            }
        }

        /// <summary>
        /// The area that the node belongs to.
        /// </summary>
        [DataMember(EmitDefaultValue = false)]
        public string SiteArea
        {
            get { return _siteArea; }
            set
            {
                if (!string.Equals(_siteArea, value, StringComparison.InvariantCulture))
                {
                    _siteArea = value;
                    NotifyPropertyChanged(() => SiteArea);
                }
            }
        }

        /// <summary>
        /// Indicates as to if the node is enabled.
        /// </summary>
        [DataMember]
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                if (_isEnabled != value)
                {
                    _isEnabled = value;
                    NotifyPropertyChanged(() => IsEnabled);
                }
            }
        }

        /// <summary>
        /// Indicates as to if the node is listed in menus and other visual representations of the <see cref="SiteMap">Site Map</see>.
        /// </summary>
        [DataMember(EmitDefaultValue = false)]
        public bool IsListed
        {
            get { return _isListed; }
            set
            {
                if (_isListed != value)
                {
                    _isListed = value;
                    NotifyPropertyChanged(() => IsListed);
                }
            }
        }

        /// <summary>
        /// Allows for custom tag to be associated with the <see cref="SiteMapNode">SiteMapNode</see>
        /// </summary>
        [DataMember(EmitDefaultValue = false)]
        public object Tag { get; set; }

        /// <summary>
        /// Indicates as to if the node has one or more children.
        /// </summary>
        public bool HasChildNodes
        {
            get { return (_childNodes != null && _childNodes.Count > 0); }
        }

#endregion

#region Methods

        public abstract void Execute();

#endregion

#region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged<T>(Expression<Func<T>> propertySelector)
        {
            Guard.ArgumentNotNull(propertySelector, "propertySelector");
            PropertyChanged.Notify<T>(propertySelector);
        }

#endregion

    }
}